from utils.key import is_valid_redis_key, get_all_keys
from redisData.rServer import RS


def check_string_key(key, rClient):
    all_keys = get_all_keys(rClient)
    if key in all_keys:
        if all_keys[key] != "string":
            return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"


# set函数 set key value
def set(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)  # 移除函数名
    if len(cmd_list) == 0:  # 如果没有传参数
        return "(error) ERR wrong number of arguments for 'set' command"
    key = cmd_list.pop(0)  # 获取键
    if not is_valid_redis_key(key):  # 判断键是否合法
        return "(error) Invalid key"
    keys_dict = get_all_keys(rClient)  # 获取已经存在的所有键
    if key in keys_dict:
        # 如果键存在且键类型为string
        if keys_dict[key] == 'string':
            if len(cmd_list) == 1:  # 一个键只能对应一个值
                sds_data[key] = cmd_list[0]
                return "OK"
            else:
                return "(error) ERR syntax error"
        else:
            # 类型不匹配不可进行修改，返回类型错误
            return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"
    # 键不存在，新添一个
    else:
        if len(cmd_list) == 1:  # 一个键只能对应一个值
            sds_data[key] = cmd_list[0]
            return "OK"
        else:
            return "(error) ERR syntax error"


def setnx(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        all_keys = get_all_keys(rClient)
        if key not in all_keys:
            if len(cmd_list) == 1:
                sds_data[key] = cmd_list[0]
                return "(integer) 1  "
            else:
                return "(error) ERR wrong number of arguments for 'setnx' command   "
        else:

            if len(cmd_list) == 1:
                return "(integer) 0  "
            else:
                return "(error) ERR wrong number of arguments for 'setnx' command"
    else:
        return "(error) Invalid key"


def get(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)  # 除去函数名
    try:  # 判断是否有传输了键
        key = cmd_list[0]
        if is_valid_redis_key(key):  # 判断键是否合法
            check_string_key(key, rClient)
            value = sds_data.get(key)
            if value:  # 如果有键
                return value
            else:  # 没键
                return '(nil)'
        else:
            return "(error) Invalid key"
    except:  # 如果没有传输键
        return "(error) ERR wrong number of arguments for 'get' command"


def getset(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    try:
        key = cmd_list[0]
        if is_valid_redis_key(key):
            key = cmd_list[0]
            cmd_list.pop(0)
            check_string_key(key, rClient)
            if len(cmd_list) == 1:
                value = sds_data.get(key)
                if value:
                    sds_data[key] = cmd_list[0]
                    return value
                else:
                    sds_data[key] = cmd_list[0]
                    return '(nil)'
            else:
                return "(error) ERR wrong number of arguments for 'getset' command"
        else:
            return "(error) Invalid key"
    except:
        return "(error) ERR wrong number of arguments for 'getset' command"


def append(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        if len(cmd_list) == 1:
            value = cmd_list[0]
            all_keys = get_all_keys(rClient)
            if key in all_keys:
                sds_data[key] = sds_data[key] + str(value)
                return "(integer)" + str(len(sds_data[key]))  # 返回字长
            else:
                sds_data[key] = value
                return "(integer) " + str(len(value))

        else:
            return "(error) ERR wrong number of arguments for 'append' command"
    else:
        return "(error) Invalid key"


def setrange(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        all_keys = get_all_keys(rClient)
        if key in all_keys:
            if len(cmd_list) == 2:
                offset = cmd_list[0]  # offset 偏移量
                cmd_list.pop(0)
                value = cmd_list[0]
                old_value = sds_data[key]
                if offset.isdigit():  # 判断offset 是否为数字
                    new_value = old_value[:int(offset)] + value
                    sds_data[key] = new_value
                    return "(integer)" + str(len(new_value))
                else:
                    return "(error) ERR value is not an integer or out of range"
            else:
                return "ERR wrong number of arguments for 'setrange' command"
        else:
            return "key is not exist"
    else:
        return "(error) Invalid key"


def getrange(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        if len(cmd_list) == 2:
            all_keys = get_all_keys(rClient)
            start = cmd_list[0]
            end = cmd_list[1]
            if start.isdigit() and end.isdigit():
                if key in all_keys:
                    value = sds_data[key]
                    return value[int(start):int(end)]  # 返回区间值
                else:
                    return "key is not exist"
            else:
                return "(error) ERR value is not an integer or out of range    "
        else:
            return "(error) ERR wrong number of arguments for 'getrange' command   "
    else:
        return "(error) Invalid key"


def incr(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        if len(cmd_list) == 0:
            all_keys = get_all_keys(rClient)
            if key in all_keys:
                if int(sds_data[key]) or int(sds_data[key], 10) == 0:
                    sds_data[key] = str(int(sds_data[key], 10) + 1)  # 键值+1
                    return "(integer) " + str(sds_data[key])
                else:
                    return "(error) ERR value is not an integer or out of range "
            else:
                sds_data[key] = "1"
                return "(integer) 1"
        else:
            return "(error) ERR wrong number of arguments for 'incr' command "
    else:
        return "(error) Invalid key"


def decr(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        if len(cmd_list) == 0:
            all_keys = get_all_keys(rClient)
            if key in all_keys:
                if int(sds_data[key]) or int(sds_data[key], 10) == 0:
                    sds_data[key] = str(int(sds_data[key], 10) - 1)  # 键值-1
                    return "(integer) " + str(sds_data[key])
                else:
                    return "(error) ERR value is not an integer or out of range "
            else:
                sds_data[key] = "-1"
                return "(integer) -1"
        else:
            return "(error) ERR wrong number of arguments for 'decr' command "

    else:
        return "(error) Invalid key"


def incrby(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        if len(cmd_list) == 1:
            all_keys = get_all_keys(rClient)
            increment = cmd_list[0]
            if int(increment) or int(increment, 10) == 0:
                if key in all_keys:
                    if int(sds_data[key], 10) or int(sds_data[key]) == 0:
                        sds_data[key] = str(int(sds_data[key], 10) + int(increment))
                        return "(integer) " + str(sds_data[key])
                    else:
                        return "(error) ERR value is not an integer or out of range "
                else:
                    sds_data[key] = "1"
                    return "(integer) 1"
            else:
                return "(error) ERR value is not an integer or out of range "
        else:
            return "(error) ERR wrong number of arguments for 'incrby' command "
    else:
        return "(error) Invalid key"


def decrby(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        if len(cmd_list) == 1:
            all_keys = get_all_keys(rClient)
            decrement = cmd_list[0]
            if int(decrement) or int(decrement, 10) == 0:
                if key in all_keys:
                    if sds_data[key].isdigit():
                        sds_data[key] = str(int(sds_data[key], 10) - int(decrement))
                        return "(integer) " + str(sds_data[key])
                    else:
                        return "(error) ERR value is not an integer or out of range "
                else:
                    sds_data[key] = "-1"
                    return "(integer) -1"
            else:
                return "(error) ERR value is not an integer or out of range "
        else:
            return "(error) ERR wrong number of arguments for 'decrby' command "
    else:
        return "(error) Invalid key"


def incrbyfloat(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    cmd_list.pop(0)
    if is_valid_redis_key(key):
        check_string_key(key, rClient)
        if len(cmd_list) == 1:
            all_keys = get_all_keys(rClient)
            increment = cmd_list[0]
            if float(increment) or float(increment) == 0:
                if key in all_keys:
                    if float(sds_data[key]) or float(sds_data[key]) == 0:
                        sds_data[key] = str(float(sds_data[key]) + float(increment))
                        return "(integer) " + str(sds_data[key])
                    else:
                        return "(error) ERR value is not an integer or out of range "
                else:
                    sds_data[key] = "1"
                    return "(integer) 1"
            else:
                return "(error) ERR value is not an integer or out of range "
        else:
            return "(error) ERR wrong number of arguments for 'incrby' command "
    else:
        return "(error) Invalid key"


def mset(cmd_list, rClient):  # 同时设置多个键值
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)

    if len(cmd_list) % 2 == 0:  # 判断是否为成对
        for i in range(0, len(cmd_list), 2):
            key = cmd_list[i]
            check_string_key(key, rClient)
            value = cmd_list[i + 1]
            sds_data[key] = value
        return "OK"
    else:
        return "(error) ERR wrong number of arguments for MSET  "


def msetnx(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    flag = True
    all_keys = get_all_keys(rClient)
    cmd_list.pop(0)
    if len(cmd_list) % 2 == 0:
        key_list = []
        value_list = []
        for key in cmd_list:
            check_string_key(key, rClient)
            key_list.append(cmd_list.pop(0))
            value_list.append(cmd_list.pop(0))
            if key in all_keys:
                flag = False
                break
            else:
                continue
        if flag == True:
            for key, value in key_list, value_list:
                sds_data[key] = value
            return "(integer) 1"
        else:
            return "(integer) 0"

    else:
        return "(error) ERR wrong number of arguments for MSETNX  "


def mget(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    if len(cmd_list) == 0:
        return "(error) ERR wrong number of arguments for 'mget' command  "
    else:
        value_list = []
        all_keys = get_all_keys(rClient)
        for index, key in enumerate(cmd_list):
            check_string_key(key, rClient)
            if key in all_keys:
                value = sds_data[key]
                value_list.append(f"{index + 1}) {value}")
            else:
                value_list.append(f"{index + 1}) (nil)")
        return "\n".join(value_list)


def delete(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    if len(cmd_list) == 0:
        return "(error) ERR wrong number of arguments for 'delete' command"
    else:
        num = 0
        all_keys = get_all_keys(rClient)
        for key in cmd_list:
            check_string_key(key, rClient)
            if key in all_keys:
                del sds_data[key]
                num += 1
        return "(integer)" + str(num)


def strlen(cmd_list, rClient):
    sds_data = RS.rDB[rClient.redisDbId].sds_data
    cmd_list.pop(0)
    key = cmd_list[0]
    if len(cmd_list) == 1:
        all_keys = get_all_keys(rClient)
        check_string_key(key, rClient)
        if key in all_keys:
            return "(integer)" + str(len(sds_data[key]))
        else:
            return "(integer) 0"
    else:
        return "(error) ERR wrong number of arguments for 'strlen' command   "


